
package com.sie.algorithm.sm;

import java.math.BigInteger;

import com.sie.algorithm.crypto.digests.SM3Digest;
import com.sie.algorithm.math.ec.ECPoint;
import com.sie.zhq.tool.ByteTool;

/**
 * SM234工具类
 *
 * @author zhq
 * @since 2016-04-01
 */
public class SMUtil {
    /**
     * 生成SM2的公私钥对
     *
     * @return SM2keyPair
     */
    public static SM2keyPair generatorSm2KeyPair() {
        SM2 sm2 = new SM2();
        return sm2.generateKeyPair();
    }

    /**
     * 根据私钥生成公钥
     *
     * @param keyPair
     * @return
     */
    public static SM2keyPair generatorSm2KeyPair(SM2keyPair keyPair) {
        SM2 sm2 = new SM2();
        BigInteger k = new BigInteger(keyPair.prvk, 16);
        @SuppressWarnings("static-access")
        ECPoint userKey = sm2.ecc_point_g.multiply(k);
        System.out.println("x:" + userKey.normalize().getXCoord().toString());
        System.out.println("Y:" + userKey.normalize().getYCoord().toString());
        keyPair.pukX = userKey.normalize().getXCoord().toString();
        keyPair.pukY = userKey.normalize().getYCoord().toString();
        return keyPair;
    }

    /**
     * 用公钥加密数据 cipherMode1：c1+c2+c3, 2：c1+c3+c2
     *
     * @param pubX
     * @param pubY
     * @param data
     * @param cipherMode
     * @return
     */
    public static String SM2PubEncrypt(byte[] data, String pubX, String pubY,
                                       int cipherMode) {
        SM2 sm2 = new SM2();
        @SuppressWarnings("static-access")
        ECPoint point = sm2.CreatePoint(pubX, pubY);
        String ciphertext = sm2.Encrypt(point, data, cipherMode);
        return ciphertext;
    }

    /**
     * 用私钥解码数据 cipherMode1：c1+c2+c3, 2：c1+c3+c2
     *
     * @return
     */
    public static byte[] SM2PrvDecrypt(String prvk, String data, int cipherMode) {
        SM2 sm2 = new SM2();
        BigInteger k = new BigInteger(prvk, 16);
        return sm2.Decrypt(k, data, cipherMode);
    }

    /**
     * 使用私钥签名
     *
     * @param userID
     * @param sourceData
     * @param prvk
     * @return
     */
    public static String SM2Sign(String userID, byte[] sourceData, String prvk) {
        byte[] c = SM2.sign(userID.getBytes(), prvk, sourceData);
        return ByteTool.byte2hex(c);
    }

    /**
     * 使用公钥验证签名
     *
     * @param pubX
     * @param pubY
     * @param sourceData
     * @param signData
     * @return
     */
    public static boolean SM2verifySign(String userID, String pubX,
                                        String pubY, byte[] sourceData, String signData) {

        return SM2.verifySign(userID.getBytes(), pubX, pubY, sourceData,
                ByteTool.hexStr2Bytes(signData));
    }

    /**
     * SM3杂凑摘要算法
     *
     * @param source
     * @return
     */
    public static String SM3Digest(byte[] source) {
        SM3Digest sm3 = new SM3Digest();
        sm3.update(source, 0, source.length);
        byte[] md = new byte[32];
        sm3.doFinal(md, 0);
        return ByteTool.byte2hex(md);

    }

    /**
     * ECB方式加密
     *
     * @param plainText
     * @param secretKey
     * @return
     */
    public static String SM4EncryptECB(String plainText, String secretKey) {
        try {
            SM4_Context ctx = new SM4_Context();
            ctx.isPadding = false;
            ctx.mode = SM4.SM4_ENCRYPT;
            byte[] keyBytes = ByteTool.hexStr2Bytes(secretKey);
            SM4 sm4 = new SM4();
            sm4.sm4_setkey_enc(ctx, keyBytes);
            byte[] encrypted = sm4.sm4_crypt_ecb(ctx,
                    ByteTool.hexStr2Bytes(plainText));
            String cipherText = ByteTool.byte2hex(encrypted);
            return cipherText;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * ECB方式解密
     *
     * @param
     * @param secretKey
     * @return
     */
    public static String SM4DecryptECB(String cipherText, String secretKey) {
        try {
            SM4_Context ctx = new SM4_Context();
            ctx.isPadding = false;
            ctx.mode = SM4.SM4_DECRYPT;

            byte[] keyBytes = ByteTool.hexStr2Bytes(secretKey);


            SM4 sm4 = new SM4();
            sm4.sm4_setkey_dec(ctx, keyBytes);
            byte[] decrypted = sm4.sm4_crypt_ecb(ctx, ByteTool.hexStr2Bytes(cipherText));
            return ByteTool.byte2hex(decrypted);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * CBC方式加密
     *
     * @param plainText
     * @param secretKey
     * @return
     */
    public static String SM4EncryptCBC(String plainText, String secretKey) {
        try {
            SM4_Context ctx = new SM4_Context();
            ctx.isPadding = false;
            ctx.mode = SM4.SM4_ENCRYPT;
            byte[] keyBytes = ByteTool.hexStr2Bytes(secretKey);
            byte[] ivBytes = ByteTool.hexStr2Bytes("00000000000000000000000000000000");
            SM4 sm4 = new SM4();
            sm4.sm4_setkey_enc(ctx, keyBytes);
            byte[] encrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, ByteTool.hexStr2Bytes(plainText));
            String cipherText = ByteTool.byte2hex(encrypted);
            return cipherText;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * CBC方式解密
     *
     * @param
     * @param secretKey
     * @return
     */
    public static String SM4DecryptCBC(String cipherText, String secretKey) {
        try {
            SM4_Context ctx = new SM4_Context();
            ctx.isPadding = false;
            ctx.mode = SM4.SM4_DECRYPT;

            byte[] keyBytes = ByteTool.hexStr2Bytes(secretKey);
            byte[] ivBytes = ByteTool.hexStr2Bytes("00000000000000000000000000000000");

            SM4 sm4 = new SM4();
            sm4.sm4_setkey_dec(ctx, keyBytes);
            byte[] decrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, ByteTool.hexStr2Bytes(cipherText));
            return ByteTool.byte2hex(decrypted);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) throws Exception {

        String str = "helloworld";
        // 生成公私
        SM2keyPair keypair = generatorSm2KeyPair();
        System.out.println("prvk:" + keypair.prvk);
        System.out.println("pukX:" + keypair.pukX);
        System.out.println("pukY:" + keypair.pukY);
        // 用公钥加密
        String ciphertext = SM2PubEncrypt(str.getBytes(), keypair.pukX,
                keypair.pukY, 1);
        System.out.println("ciphertext:" + ciphertext);
        // 私钥解密
        byte[] b = SM2PrvDecrypt(keypair.prvk, ciphertext, 1);

        System.out.println("plaintext:" + new String(b));
        // 签名
        String userid = "1234567812345678";
        String sign = SM2Sign(userid, str.getBytes(), keypair.prvk);
        // 验证签名
        boolean isVerify = SM2verifySign(userid, keypair.pukX, keypair.pukY,
                str.getBytes(), sign);

        System.out.println("验证结果：" + isVerify);

        // sm3摘要

        String hexResult = SM3Digest(str.getBytes());
        System.out.println("sm3摘要：" + hexResult);

        String plaintext = "FEDCBA98765432100123456789ABCDEFFEDCBA98765432100123456789ABCDEF";

        String key = "0123456789ABCDEFFEDCBA9876543210";


        ciphertext = SM4EncryptECB(plaintext, key);

        System.out.println("sm4ECB加密：" + ciphertext);

        plaintext = SM4DecryptECB(ciphertext, key);

        System.out.println("sm4ECB解密：" + plaintext);


        ciphertext = SM4EncryptCBC(plaintext, key);

        System.out.println("sm4CBC加密：" + ciphertext);

        plaintext = SM4DecryptCBC(ciphertext, key);

        System.out.println("sm4CBC解密：" + plaintext);

    }

}
